<?PHP
namespace SDF;

use SDF\Core\Config,
    SDF\Core\Route,
    SDF\Core\View;

/**
 * 框架基类
 * @package SDF
 */
class SDF {
    /**
     * 项目目录
     * @var string
     */
    private static $rootPath;//项目所在目录(入口文件目录)
    /**
     * 配置目录
     * @var string
     */
    private static $configPath = 'default';     //默认配置目录
    private static $appPath = 'application';    //APP所在目录
    private static $sdfPath;                    //框架所在目录绝对路径（有时候和项目所在目录一致）
    private static $classPath = array();        //加载器类的目录列表

    /**
     * 获取项目所在目录
     * @return string
     */
    public static function getRootPath(){
        return \rtrim(self::$rootPath,DS).DS;
    }

    /**
     * 设置项目所在目录
     * @param $rootPath
     */
    public static function setRootPath($rootPath){
        self::$rootPath = $rootPath;
    }

    /**
     * 获取配置文件所在目录
     * @return string
     */
    public static function getConfigPath(){
        $dir = self::getRootPath() . 'config' . DS . self::$configPath;
        if (\is_dir($dir)) {
            return $dir;
        }
        return self::getRootPath() . 'config' . DS . 'default';
    }

    /**
     * 设置配置文件目录
     * @param $path
     */
    public static function setConfigPath($path){
        self::$configPath = $path;
    }

    public static function getAppPath(){
        return self::$appPath;
    }

    public static function setAppPath($path){
        self::$appPath = $path;
    }

    public static function getsdfPath(){
        return self::$sdfPath;
    }

    /**
     * 自动加载
     * @param $class
     * @return bool
     */
    final public static function autoLoader($class){
        if (isset(self::$classPath[$class])) {//已经加载过的。不需要再加载了。
            return true;
        }
        $baseClasspath = \str_replace('\\', DS, $class) . '.php';
        $libs = array(
            self::$rootPath . DS . self::$appPath,//首先到应用程序目录中查找。
            self::$sdfPath,//其次再到框架目录中查找
        );
        foreach ($libs as $lib) {
            $classpath = $lib . DS . $baseClasspath;
            if (\is_file($classpath)) {
                self::$classPath[$class] = $classpath;
                require "{$classpath}";
                return true;
            }
        }
        return false;
    }

    /**
     * 自定义的异常处理函数
     * @param \Exception $exception
     */
    final public static function exceptionHandler($exception){
        if (!empty($_SERVER['argv'][1])) {
            $msg = "<b>Exception:</b>".$exception->getCode() .':'. $exception->getMessage().' in '.$exception->getFile().' on line '.$exception->getLine()."\n";
            echo $msg;
        }elseif(IS_AJAX){
            echo json_encode(
                array(
                    'status_code'=>300,//$exception->getCode(),
                    'message'=>$exception->getMessage(),
                    'file'=>$exception->getFile(),
                    'line'=>$exception->getLine(),
                    'response'=>array(),
                )
            );exit;
        }else{
            $error_code = $exception->getCode();
            self::set_status_header($error_code);
            $msg = ['msg'=>"<b>Exception:</b>".$exception->getCode() .':'. $exception->getMessage().' in '.$exception->getFile().' on line '.$exception->getLine()."\n"];
            if($error_code == 404){
                View::display($msg,'_show404');exit;
            }
            View::display($msg ,'_error');exit;
        }
    }

    /**
     * 设置http状态码
     * @param int $code
     * @param string $text
     */
    final public static function set_status_header($code = 200, $text = ''){
        $static = array(
            200	=> 'OK',
            201	=> 'Created',
            202	=> 'Accepted',
            203	=> 'Non-Authoritative Information',
            204	=> 'No Content',
            205	=> 'Reset Content',
            206	=> 'Partial Content',

            300	=> 'Multiple Choices',
            301	=> 'Moved Permanently',
            302	=> 'Found',
            304	=> 'Not Modified',
            305	=> 'Use Proxy',
            307	=> 'Temporary Redirect',

            400	=> 'Bad Request',
            401	=> 'Unauthorized',
            403	=> 'Forbidden',
            404	=> 'Not Found',
            405	=> 'Method Not Allowed',
            406	=> 'Not Acceptable',
            407	=> 'Proxy Authentication Required',
            408	=> 'Request Timeout',
            409	=> 'Conflict',
            410	=> 'Gone',
            411	=> 'Length Required',
            412	=> 'Precondition Failed',
            413	=> 'Request Entity Too Large',
            414	=> 'Request-URI Too Long',
            415	=> 'Unsupported Media Type',
            416	=> 'Requested Range Not Satisfiable',
            417	=> 'Expectation Failed',

            500	=> 'Internal Server Error',
            501	=> 'Not Implemented',
            502	=> 'Bad Gateway',
            503	=> 'Service Unavailable',
            504	=> 'Gateway Timeout',
            505	=> 'HTTP Version Not Supported'
        );
        if ($code == '' OR ! is_int($code)){
            $code = 500;
        }
        if (isset($static[$code]) AND $text == ''){
            $text = $static[$code];
        }
        if ($text == ''){
            $text = 'No status text available.  Please check your status code number or supply your own message text.';
        }
        $server_protocol = (isset($_SERVER['SERVER_PROTOCOL'])) ? $_SERVER['SERVER_PROTOCOL'] : FALSE;
        if (substr(php_sapi_name(), 0, 3) == 'cgi'){
            \header("Status: {$code} {$text}", TRUE);
        }elseif ($server_protocol == 'HTTP/1.1' OR $server_protocol == 'HTTP/1.0'){
            \header($server_protocol." {$code} {$text}", TRUE, $code);
        }else{
            \header("HTTP/1.1 {$code} {$text}", TRUE, $code);
        }
    }

    /**
     * 以路由模式启动框架
     * @param $rootPath
     * @throws \Exception
     */
    public static function run($rootPath){
        self::start($rootPath);
        Route::route_start();
    }

    /**
     * 不使用路由器启动框架
     * 如果您只是想使用框架里面的类库。就可以使用此方式加载
     * @param $rootPath
     * @throws \Exception
     */
    public static function start($rootPath){
        self::initSDFConf();
        self::setRootPath($rootPath);

        if (!empty($_SERVER['HTTP_HOST'])) {
            $configPath = \str_replace(':', '_', $_SERVER['HTTP_HOST']);
        } elseif (!empty($_SERVER['argv'][1])) {
            $parse_url = \parse_url($_SERVER['argv'][1]);
            if(isset($parse_url['host'])) $configPath = \str_replace(':', '_', $parse_url['host']);
            if(isset($parse_url['path'])) $_SERVER['PATH_INFO'] = $parse_url['path'];
        }
        if (!empty($configPath)) {
            self::setConfigPath($configPath);
        }
        \spl_autoload_register(__CLASS__ . '::autoLoader');

        Config::load(self::getConfigPath());//加载配置文件

        $appPath = Config::get('app_path', self::$appPath);
        self::setAppPath($appPath);
        $eh = Config::getField('project', 'exception_handler', __CLASS__ . '::exceptionHandler');//获取配置文件中配置的异常处理函数
        \set_exception_handler($eh);//函数设置用户自定义的异常处理函数。该函数用于创建运行时期间的用户自己的异常处理方法。该函数会返回旧的异常处理程序，若失败，则返回 null。
        $timeZone = Config::get('time_zone', 'Asia/Chongqing');
        \date_default_timezone_set($timeZone);//设置时区
    }

    /**
     * 初始化功能配置文件，主要用于初始化多项目时需要使用的公共配置
     */
    private static function initSDFConf(){
        if (!defined('IS_AJAX')){//IS_AJAX (是否是ajax请求)
            define('IS_AJAX', ((isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest')) ? true : false);

        }
        if (!defined('DS')){//定义简写的文件分隔符
            define('DS', DIRECTORY_SEPARATOR);
        }
        self::$sdfPath = \dirname(__DIR__);//设置框架所在目录

        if (!defined('DOMAIN_NAME')) {
            #判断运行环境是生产环境还是测试环境。从而设置常量“DOMAIN_NAME(域名常量)”
            if (!empty(get_cfg_var( 'DOMAIN_NAME'))){
                define('DOMAIN_NAME', get_cfg_var( 'DOMAIN_NAME'));
            }
        }
        //加载框架配置文件。框架配置文件是集群公共配置。属于公共文件，应该由运行环境配置。目前使用的加载顺序为：PHP.ini配置的SDF_CONF环境变量，如果不存在，者加载【服务器IPsdf_conf.php】
        $sdf_conf_file = self::$sdfPath.DS.get_cfg_var( 'SDF_CONF_FILE');
        if(!empty($sdf_conf_file) and is_file($sdf_conf_file.'.php')){
            include_once $sdf_conf_file;
        }else{
            $sdf_conf_file = self::$sdfPath.DS.'SDFConf.php';
            if(is_file($sdf_conf_file)){
                include_once $sdf_conf_file;
            }else{
                echo 'Please copy '.self::$sdfPath.DS.'SDFConf.development.php to '.$sdf_conf_file;exit;
            }

        }

    }
}
